package com.googlecode.frank.driver;

import static com.googlecode.frank.driver.Matchers.json;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertThat;

import java.io.IOException;
import java.io.StringWriter;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.junit.Test;


/**
 * @author evansg
 */
public abstract class JsonTester<T> {

    private final Class<T> classUnderTest;

	public JsonTester(Class<T> classUnderTest) {
		this.classUnderTest = classUnderTest;
	}

	@Test
	public void testEqualityAfterSerializationAndDeserialization() throws IOException {
		assertThat("Class under test cannot be null", createTestObject(), is(notNullValue()));
		String json = serialize(createTestObject());
		assertThat("Generated Json cannot be null", json, is(notNullValue()));
		T t = deserialize(json, classUnderTest);

		assertThat("Objects are not equal", createTestObject(), is(t));
		assertThat("Object toStrings are not equal", createTestObject().toString(),
				is(t.toString()));
		assertThat("Object hashCodes are not equal", createTestObject().hashCode(),
				is(t.hashCode()));
	}

	@Test
	public void printObjectToLog() throws IOException {
		String generatedJson = serializePrettyString(createTestObject());
		System.out.println(generatedJson);
	}

	@Test
	public void testSerialization() throws IOException {
		String generatedJson = serialize(createTestObject());
		assertThat("Generated Json is does not match", createTestObjectAsJson(),
				is(json(generatedJson)));
	}

	@Test
	public void testDeserialization() throws IOException {
		T fromJson = deserialize(createTestObjectAsJson(), classUnderTest);
		assertThat("Objects do not match", fromJson, is(createTestObject()));
	}

	@Test
	public void testIsValid() {
		T t = createTestObject();
		Set<ConstraintViolation<T>> violations = validate(t);
		assertThat(violations.size(), is(0));
	}

	public abstract T createTestObject();

	public abstract String createTestObjectAsJson();

	protected Set<ConstraintViolation<T>> validate(T dto) {
		ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
		Validator validator = validatorFactory.usingContext().getValidator();
        return validator.validate(dto);
	}

	private T deserialize(String json, Class<T> clazz) throws IOException {
		ObjectMapper mapper = new ObjectMapper();
		return mapper.readValue(json, clazz);
	}

	private String serializePrettyString(T t) throws IOException {
		ObjectMapper mapper = new ObjectMapper();
		mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
		StringWriter writer = new StringWriter();
		mapper.writeValue(writer, t);
		return writer.toString();
	}

	private String serialize(T t) throws IOException {
		ObjectMapper mapper = new ObjectMapper();
		StringWriter writer = new StringWriter();
		mapper.writeValue(writer, t);
		return writer.toString();
	}



}
